package org.dlsu.mt.parser;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Vector;

import org.dlsu.mt.DictionaryInterface;
import org.dlsu.mt.MorphInterface;
import org.dlsu.mt.SentenceTokenizerInterface;
import org.dlsu.mt.parser.generator.ParseTable;

/**
 * NLPParser is a parser made for natural langauges. It has 5 main components,
 * the Sentence Tokenizer, Lexical Analyzer, Parser Engine, Dictionary, and Morphological Analyzer.
 * The sentence tokenizer divides the entire document into sentences.
 * The lexical analyzer divides each sentence into tokens.
 * The dictionary is used to look up the words analyzed by the lexical analyzer to attach semantics.
 * The morphological analyzer is also used by the lexical analyzer to get the root word.
 * The parser engine finally analyzes the tokens produced by the lexical analyzer and produces different parse trees.
 *
 * The components required and are checked by the class are the sentence tokenizer, lexical analyzer, and parser engine.
 * As for the the dictionary and morphological analyzer, it will rely on the lexical analyzer implementation used
 * whether it is necessary or not.
 *
 * To use the NLPParser, you instantiate it with its corresponding parts using the constructor:
 *	<code> new NLPParser(SentenceTokenizerInterface senTok, LexicalAnalyzerInterface lexer, ParserEngineInterface parser, DictionaryInterface dict, MorphInterface morph) </code>
 * or set it manually like so:
 *	<code>
 *		NLPParser nlp = new NLPParser();
 *		nlp.setLexer(lexer);
 *		nlp.setParser(parser);
 *	</code>
 *
 * Then you set the document for it parse by calling the setDocument() method:
 *		<code> setDocument("Sentence 1\nSentence 2"); </code>
 *
 * Finally you can iterate through the list of sentences using hasMoreSentences() and getting the equivalent parse tree using parseSentence()
 * 	<code>
 *		Vector syntaxTrees = new Vector();
 *		while(nlp.hasMoreSentences())
 *			syntaxTrees.addElement(nlp.parseSentence());
 *	</code>
 * or to retrieve all the parse trees of all sentences at once you can use
 *	<code>
 *		Vector syntaxTrees = nlp.parse();
 *	</code>
 * or to save all the parse trees in the document to a file in XML format use
 *	<code>
 *		nlp.saveParseResultToFile(filename);
 *	</code>
 */
public class NLPParser {
	
	public static char cLang;
	public static String strText;
	/**
	 * Parser's Sentence Tokenizer
	 */
	protected SentenceTokenizerInterface senTok = null;
	/**
	 * Parser's Lexical Analyzer
	 */
	protected LexicalAnalyzerInterface lexer=null;
	/**
	 * Parser's Parser Engine
	 */
	protected ParserEngineInterface parser=null;
	/**
	 * Parser's Dictionary
	 */
	protected DictionaryInterface dict = null;
	/**
	 * Parser's Morphological Analyzer
	 */
	protected MorphInterface morph = null;
	/**
	 * Parser's Parse Table
	 */
	protected ParseTable pt = null;
	/**
	 * List of sentences for the parser to parse
	 */
	protected Vector sentences = new Vector();
	/**
	 * Current sentence to parse
	 */
	protected int marker=-1;

	/**
	 * Creates an instance of an NLPParser with its necessary components
	 *
	 * @param lexer The lexical analyzer to be used
	 * @param parser The parser engine to be used
	 * @param dict The dictionary to be used
	 * @param morph The morphological analyzer to be used
	 * @param senTok The sentence tokenizer to use
	 **/
	public NLPParser(SentenceTokenizerInterface senTok, LexicalAnalyzerInterface lexer, ParserEngineInterface parser, DictionaryInterface dict, MorphInterface morph, ParseTable pt) {
		this.morph = morph;
		this.lexer = lexer;
		this.parser = parser;
		this.dict = dict;
		this.senTok = senTok;
		this.pt = pt;
	}

	/**
	 * Creates an instance of an NLPParser with its components manually set later on
	 **/
	public NLPParser() {
	}

	/**
	 * Sets the parse table to use
	 *
	 * @param pt The parse table
	 */
	public void setParseTable(ParseTable pt) {
		this.pt = pt;
	}

	/**
	 * Sets the sentence tokenizer to use
	 *
	 * @param senTok The sentence tokenizer to use
	 */
	public void setSentenceTokenizer(SentenceTokenizerInterface senTok) {
		this.senTok = senTok;
	}

	/**
	 * Sets the lexical analyzer to use
	 *
	 * @param lexer The lexical analyzer to use
	 */
	public void setLexer(LexicalAnalyzerInterface lexer) {
		this.lexer = lexer;
	}

	/**
	 * Sets the parser engine to use
	 *
	 * @param parser The parser engine to use
	 */
	public void setParser(ParserEngineInterface parser) {
		this.parser = parser;
	}

	/**
	 * Sets the dictionary to use
	 *
	 * @param dict The dictionary to use
	 */
	public void setDictionary(DictionaryInterface dict) {
		this.dict = dict;
	}

	/**
	 * Sets the morphological analyzer to use
	 *
	 * @param morph The morpohological analyzer to use
	 */
	public void setMorph(MorphInterface morph) {
		this.morph = morph;
	}

	/**
	 * Sets the document to parse
	 *
	 * @param document Document to parse
	 */
	public void setDocument(Object document) throws Exception {

		if(senTok == null)
			throw new RuntimeException("No Sentence Tokenizer Set");

		sentences.clear();

		senTok.setDocument(document);

		while(senTok.hasMoreSentences()) {
			sentences.add(senTok.nextSentence());
		}

		marker = (sentences.size() == 0) ? -1 : 0;
	}

	/**
	 * Parses a sentence from the given document, returns its parse tree and moves to the next sentence
	 *
	 * @return The parse tree of the sentence, <code> null </code> if there are no more sentences
	 */
	public Vector parseSentence() throws Exception{
		LexerInterface lex;
		if(lexer == null)
			throw new RuntimeException("No Lexical Analyzer Set");
		if (parser == null)
			throw new RuntimeException("No Parsing Engine Set");
		if (pt == null)
			throw new RuntimeException("No Parse Table Set");
		if (!hasMoreSentences())
			return null;

		lexer.setDictionary(dict);
		lexer.setMorph(morph);

		lex = lexer.analyze((String) sentences.elementAt(marker));
		marker++;

		parser.reset();
		parser.setLexer(lex);
		parser.setParseTable(pt);
		return parser.parse();
	}

	/**
	 * Parses the entire document and returns a Vector of Vectors containing all the syntax trees
	 * from the first to the last sentence.
	 */
	public Vector parse() throws Exception {
		LexerInterface lex;
		Vector v = new Vector();
		
		
		reset();
		
		if(lexer == null)
			throw new RuntimeException("No Lexical Analyzer Set");
		if (parser == null)
			throw new RuntimeException("No Parsing Engine Set");
		if (pt == null)
			throw new RuntimeException("No Parse Table Set");
		if (!hasMoreSentences())
			return null;
		
		
		lexer.setDictionary(dict);
		lexer.setMorph(morph);
		parser.setParseTable(pt);

		
		while(hasMoreSentences()) {
			
			
			lex = lexer.analyze((String) sentences.elementAt(marker));
			
			marker++;

			parser.reset();
			parser.setLexer(lex);

			v.add(parser.parse());
		}
		
		return v;
	}

	/**
	 * Checks if there are more sentences to parse from the document
	 *
	 * @return true if there are more sentences to parse, otherwise false
	 */
	public boolean hasMoreSentences() {
		return !(sentences.size()==marker || marker ==-1);
	}

	/**
	 * Gets the total number of sentences inside the document set
	 *
	 * @return Total number of sentences
	 */
	public int getSentenceTotal() {
		return sentences.size();
	}

	/**
	 * Gets the sentence at the specified index
	 *
	 * @param index Sentence index, 0-indexed
	 * @return The sentence at the specified index
	 */
	public String getSentence(int index) {
		return (String) sentences.elementAt(index);
	}

	/**
	 * Resets the NLPParser to parse the first sentence when parseSentence() is called.
	 */
	public void reset() {
		marker = (sentences.size() == 0) ? -1 : 0;
	}

	/**
	 * Gets the next sentence to parse without moving to the next sentence
	 *
	 * @return The next sentence to be parsed
	 */
	public String getNextSentenceToParse() {
		return (String) sentences.elementAt(marker);
	}

	/**
	 * Skips a sentence to parse
	 */
	public void skip() {
		if (marker != sentences.size() && marker != -1)
			marker++;
	}

	/**
	 * Saves the parse result of the entire document to a file in XML format as follow:
	 * <PRE>
	 *  <DOCUMENT sentence_count=" <total number of sentences parsed>">
	 *	 	<TREES size="<total number of syntax tree for the sentence>" sentence="<sentence 1>">
	 *			<SYNTAX TREE 1>
	 *			<SYNTAX TREE 2>
	 *			...
	 *			<SYNTAX TREE N>
	 *		</TREES>
	 *	 	<TREES size="<total number of syntax tree for the sentence>" sentence="<sentence 2>">
	 *			<SYNTAX TREE 1>
	 *			<SYNTAX TREE 2>
	 *			...
	 *			<SYNTAX TREE N>
	 *		</TREES>
	 *		...
	 *	 	<TREES size="<total number of syntax tree for the sentence>" sentence="<sentence N>">
	 *			<SYNTAX TREE 1>
	 *			<SYNTAX TREE 2>
	 *			...
	 *			<SYNTAX TREE N>
	 *		</TREES>
	 *	</DOCUMENT>
	 * </PRE>
	 *
	 * @param filename The filename to save the parsed result
	 */
	public void saveParseResultToFile(String filename) throws Exception{
		PrintWriter pw = new PrintWriter(new FileOutputStream(filename));
		Vector v,v2;

		v = parse();
		pw.println("<DOCUMENT sentence_count=\""+getSentenceTotal()+"\">");
		for(int i=0;i<v.size();i++) {

			String sentence = (String) sentences.elementAt(i);
			v2 = (Vector) v.elementAt(i);

			pw.println("<TREES size=\""+v2.size()+"\" sentence=\""+sentence+"\">");

			for(int j=0;j<v2.size();j++) {
				pw.println(((Node) v2.elementAt(j)).displayXML());
			}
			pw.println("</TREES>");
		}
		pw.println("</DOCUMENT>");
		pw.close();
	}

	/**
	 * Saves the parse tree result of the entire document to a file in XML format similar to saveParserResultToFile(String),
	 * and saves the semantics to another file as follows:
	 * <PRE>
	 *  <DOCUMENT sentence_count=" <total number of sentences parsed>">
	 *	 	<SEMANTICS size="<total number of semantic structures for the sentence>" sentence="<sentence 1>">
	 *			<SEMANTIC STRUCTURE 1>
	 *			<SEMANTIC STRUCTURE 2>
	 *			...
	 *			<SEMANTIC STRUCTURE N>
	 *		</SEMANTICS>
	 *	 	<SEMANTICS size="<total number of semantic structure for the sentence>" sentence="<sentence 2>">
	 *			<SEMANTIC STRUCTURE 1>
	 *			<SEMANTIC STRUCTURE 2>
	 *			...
	 *			<SEMANTIC STRUCTURE N>
	 *		</SEMANTICS>
	 *		...
	 *	 	<SEMANTICS size="<total number of semantic structure for the sentence>" sentence="<sentence N>">
	 *			<SEMANTIC STRUCTURE 1>
	 *			<SEMANTIC STRUCTURE 2>
	 *			...
	 *			<SEMANTIC STRUCTURE N>
	 *		</SEMANTICS>
	 *	</DOCUMENT>
	 * </PRE>
	 *
	 * This method assumes that the interpret() method of the Node object returns an FStructure object.
	 * This also only writes the syntax trees with consistent semantic structures.
	 *
	 * @param stFilename The filename to save the parse tree of the result
	 * @param semFilename The filename to save the semantics of the result
	 */
	public void saveParseResultToFile(String stFilename, String semFilename) throws Exception{
		PrintWriter pw = new PrintWriter(new FileOutputStream(stFilename));
		PrintWriter pw2 = new PrintWriter(new FileOutputStream(semFilename));
		Vector v,v2;
		FStructure f;

		v = parse();
		pw.println("<?xml version='1.0' ?>");
		//pw.println("<!DOCTYPE DOCUMENT SYSTEM \"syntax.dtd\">");
		pw.println("<DOCUMENT sentence_count=\""+getSentenceTotal()+"\">");

		pw2.println("<?xml version='1.0' ?>");
		//pw2.println("<!DOCTYPE DOCUMENT SYSTEM \"fstructure.dtd\">");
		pw2.println("<DOCUMENT sentence_count=\""+getSentenceTotal()+"\">");
		
		for(int i=0;i<v.size();i++) {

			String sentence = (String) sentences.elementAt(i);
			v2 = (Vector) v.elementAt(i);

			pw.println("<TREES size=\""+v2.size()+"\" sentence=\""+sentence+"\">");
			pw2.println("<SEMANTICS size=\""+v2.size()+"\" sentence=\""+sentence+"\">");

			for(int j=0;j<v2.size();j++) {
				f = ((FStructure) ((Node) v2.elementAt(j)).interpret());
								
				if (f != null)
				{
					pw.println(((Node)v2.elementAt(j)).displayXML());
					pw2.println(f.toXML());	
				}
			}
			

			pw.println("</TREES>");
			pw2.println("</SEMANTICS>");
		}
		pw.println("</DOCUMENT>");
		pw2.println("</DOCUMENT>");
		pw.close();
		pw2.close();
		
	}
	
	public FStructure analyzeToFStructure() throws Exception
	{
		Vector v,v2;
		FStructure f=null, finalf = null;
		
		v = parse();
		//GIANAHOO System.out.println("Here2 " );
		for(int i=0;i<v.size();i++) {

			String sentence = (String) sentences.elementAt(i);
			v2 = (Vector) v.elementAt(i);
			for(int j=0;j<v2.size();j++)
			{
				f = ((FStructure) ((Node) v2.elementAt(j)).interpret());
				if(f != null)
				{
					//GIANAHOO System.out.println("semantics and functional tests starting...");
					f.execDownArrows(null);
					if(f.checkFunctionalSchemata()&&f.checkSemanticEntries(null))
						finalf = f;
					else
						System.out.println("Not Accepted by the Functional Schemata or Semantics");
				}
				else
					System.out.println("not accepted by grammar");
			}
		}
		
		
		//saveParseResultToFile("c:/parsetree.xml", "c:/analysisOutput.xml");
		return finalf;
	}
}

